/*
 * Copyright 2015 Eric Liu
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.liuguangqiang.swipeback;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DependentLayout;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

/**
 * Swipe or Pull to finish a Activity.
 * <p/>
 * This layout must be a root layout and contains only one direct child view.
 * <p/>
 * The activity must use a theme that with translucent style.
 * <style name="Theme.Swipe.Back" parent="AppTheme">
 * <item name="windowIsTranslucent">true</item>
 * <item name="windowBackground">@:color/transparent</item>
 * </style>
 * <p/>
 * Created by Eric on 15/1/8.
 */
public class SwipeBackLayout extends DependentLayout {
    private float mDownTouchX;
    private float mDownTouchY;
    private boolean enable = true;
    private boolean finish = false;

    /**
     * Whether allow to pull this layout.
     */
    private boolean enablePullToBack = true;

    /**
     * DragDirectMode
     */
    public enum DragDirectMode {
        EDGE,
        VERTICAL,
        HORIZONTAL
    }

    /**
     * 方向
     */
    public enum DragEdge {
        LEFT,

        TOP,

        RIGHT,

        BOTTOM
    }
    private DragDirectMode dragDirectMode = DragDirectMode.EDGE;

    private DragEdge dragEdge = DragEdge.TOP;

    public void setDragEdge(DragEdge dragEdge) {
        this.dragEdge = dragEdge;
    }

    public void setDragDirectMode(DragDirectMode dragDirectMode) {
        this.dragDirectMode = dragDirectMode;
        if (dragDirectMode == DragDirectMode.VERTICAL) {
            this.dragEdge = DragEdge.TOP;
        } else if (dragDirectMode == DragDirectMode.HORIZONTAL) {
            this.dragEdge = DragEdge.LEFT;
        }
    }

    public SwipeBackLayout(Context context) {
        this(context, null);
    }

    public SwipeBackLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public SwipeBackLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        touch();
    }

    private void touch() {
        setTouchEventListener(new TouchEventListener() {
            @Override
            public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
                MmiPoint point = touchEvent.getPointerScreenPosition(0);
                switch (touchEvent.getAction()) {
                    case TouchEvent.PRIMARY_POINT_DOWN:
                        float downTouchX = point.getX();
                        float downTouchY = point.getY();
                        mDownTouchX = downTouchX;
                        mDownTouchY = downTouchY;
                        break;
                    case TouchEvent.PRIMARY_POINT_UP:
                        touchUp();
                        break;
                    case TouchEvent.POINT_MOVE:
                        tochMove(point);
                        break;
                }
                return true;
            }
        });
    }

    private void tochMove(MmiPoint point) {
        if (!enable) {
            return;
        }
        final float moveTouchX = point.getX();
        final float moveTouchY = point.getY();
        final float dx = moveTouchX - mDownTouchX;
        final float dy = moveTouchY - mDownTouchY;
        switch (dragEdge) {
            case TOP:
                if ((getContentPositionY() + dy) > 0) {
                    setTranslationY(getContentPositionY() + dy);
                }
                break;
            case BOTTOM:
                if ((getContentPositionY() + dy) < 0) {
                    setTranslationY(getContentPositionY() + dy);
                }
                break;
            case LEFT:
                if ((getContentPositionX() + dx) > 0) {
                    if (dy < 15 && dy > -15) {
                        setTranslationX(getContentPositionX() + dx);
                    }
                }
                break;
            case RIGHT:
                if ((getContentPositionX() + dx) < 0) {
                    setTranslationX(getContentPositionX() + dx);
                }
                break;
            default:
                break;
        }
        mDownTouchX = moveTouchX;
        mDownTouchY = moveTouchY;
    }

    private void touchUp() {
        switch (dragEdge) {
            case TOP:
                if (getTranslationY() > 1500 && finishAbility != null) {
                    close(true);
                    return;
                } else {
                    open();
                }
                break;
            case BOTTOM:
                if (getTranslationY() < -1500 && finishAbility != null) {
                    close(true);
                    return;
                } else {
                    open();
                }
                break;
            case LEFT:
                if (getTranslationX() > 700 && finishAbility != null) {
                    close(false);
                    return;
                } else {
                    open();
                }
                break;
            case RIGHT:
                if (getTranslationX() < -700 && finishAbility != null) {
                    close(false);
                    return;
                } else {
                    open();
                }
                break;
            default:
                break;
        }
    }

    private void open() {
        AnimatorValue animator = new AnimatorValue();
        float tanslateY = getTranslationY();
        float tanslateX = getTranslationX();

        animator.setDuration(200);
        animator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                setTranslationY((1 - value) * tanslateY);
                setTranslationX((1 - value) * tanslateX);
            }
        });
        animator.start();
    }

    private void close(boolean tagY) {
        AnimatorValue animator = new AnimatorValue();
        float tanslateY = getTranslationY();
        float tanslateX = getTranslationX();

        float width = getWidth() - tanslateX;
        float height = getHeight() - tanslateY;

        animator.setDuration(150);
        animator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                if (tagY) {
                    setTranslationY(tanslateY + (value) * height);
                } else {
                    setTranslationX(tanslateX + (value) * width);
                }
            }
        });
        animator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                finishAbility.onFinishState();
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animator.start();
    }

    /**
     * the anchor of calling finish.
     */
    private float finishAnchor = 0;

    /**
     * Set the anchor of calling finish.
     *
     * @param offset
     */
    public void setFinishAnchor(float offset) {
        finishAnchor = offset;
    }

    private boolean enableFlingBack = true;

    /**
     * Whether allow to finish activity by fling the layout.
     *
     * @param b
     */
    public void setEnableFlingBack(boolean b) {
        enableFlingBack = b;
    }
    private SwipeBackListener swipeBackListener;

    @Deprecated
    public void setOnPullToBackListener(SwipeBackListener listener) {
        swipeBackListener = listener;
    }

    public void setOnSwipeBackListener(SwipeBackListener listener) {
        swipeBackListener = listener;
    }



    /**
     * 是否关闭页面
     */
    public OnFinishListener finishAbility;

    /**
     * 关闭页面
     *
     * @param finishAbility 回调事件
     */
    public void setOnFinishListener(OnFinishListener finishAbility) {
        this.finishAbility = finishAbility;
    }

    /**
     * 关闭ability接口
     */
    public interface OnFinishListener {
        void onFinishState();
    }

    /**
     * 设置是否滑动
     *
     * @param enable 是否滑动
     */
    public void setEnableSwipe(boolean enable) {
        this.enable = enable;
    }

    /**
     * EnablePullToBack
     * @param b enablePullToBack
     */
    public void setEnablePullToBack(boolean b) {
        enablePullToBack = b;

    }

    public interface SwipeBackListener {

        /**
         * Return scrolled fraction of the layout.
         *
         * @param fractionAnchor relative to the anchor.
         * @param fractionScreen relative to the screen.
         */
        void onViewPositionChanged(float fractionAnchor, float fractionScreen);

    }
}
